package com.lu.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lu.mapper.*;
import com.lu.model.constant.RedisKeyConstants;
import com.lu.model.domain.LoginUser;
import com.lu.model.dto.UserRoleResourcesDTO;
import com.lu.model.entity.*;
import com.lu.model.enums.SysUserTypeEnum;
import com.lu.model.enums.YesOrNoEnum;
import com.lu.model.exception.LuBootAssert;
import com.lu.model.request.*;
import com.lu.service.SysUserService;
import com.lu.utils.SecurityUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author wangqieyu
 * @since 2022-07-26
 */
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

  private final RedisTemplate<String, Object> redisTemplate;

  private final SysUserRoleMapper userRoleMapper;
  private final SysRoleMapper roleMapper;
  private final SysResourcesMapper resourcesMapper;
  private final SysDeptMapper deptMapper;
  private final SysMerchantMapper sysMerchantMapper;
  private final SysStoreMapper sysStoreMapper;

  @Override
  public void logout() {
    SysUser sysUser = (SysUser) SecurityContextHolder.getContext().getAuthentication()
        .getPrincipal();
    if (sysUser != null) {
      redisTemplate.delete(RedisKeyConstants.loginUser(sysUser.getId()));
    }
    SecurityContextHolder.clearContext();
  }

  @Override
  public Long addUser(UserAddRequest request) {
    LuBootAssert.isTrue(ObjectUtil.isEmpty(request.getStatus())
            || Arrays.stream(YesOrNoEnum.values()).anyMatch(o -> o.getValue() == request.getStatus()),
        "无效的状态参数");
    this.checkUsernameUnique(null, request.getUsername());
    this.checkPhoneUnique(null, request.getPhone());
    SysUser loginUser = (SysUser) SecurityContextHolder.getContext().getAuthentication()
        .getPrincipal();
    SysUser user = new SysUser();
    BeanUtil.copyProperties(request, user);
    String password = DigestUtils
        .md5DigestAsHex(StrUtil.bytes((user.getPassword() + SysUser.SALT), CharsetUtil.UTF_8));
    user.setPassword(password);
    user.setCreateUserId(loginUser.getId());
    user.setUpdateUserId(loginUser.getId());
    this.baseMapper.insert(user);
    return user.getId();
  }

  @Override
  public SysUser updateUser(UserUpdateRequest request) {
    LuBootAssert.isTrue(ObjectUtil.isEmpty(request.getStatus())
            || Arrays.stream(YesOrNoEnum.values()).anyMatch(o -> o.getValue() == request.getStatus()),
        "无效的状态参数");
    this.checkPhoneUnique(request.getUserId(), request.getPhone());
    this.checkUsernameUnique(request.getUserId(), request.getUsername());
    SysUser loginUser = (SysUser) SecurityContextHolder.getContext().getAuthentication()
        .getPrincipal();
    SysUser user = new SysUser();
    BeanUtil.copyProperties(request, user);
    user.setId(request.getUserId());
    user.setUpdateUserId(loginUser.getId());
    user.setUpdateTime(LocalDateTime.now());
    this.baseMapper.updateById(user);
    if (request.getStatus() != null) {
      this.updateLoginUser(request.getUserId());
    }
    SysUser updateUser = this.baseMapper.selectById(user.getId());
    return updateUser;
  }

  @Override
  public Integer updatePwd(UserUpdatePwdRequest request) {
    SysUser loginUser = (SysUser) SecurityContextHolder.getContext().getAuthentication()
        .getPrincipal();
    SysUser user = this.baseMapper.selectById(request.getUserId());
    LuBootAssert.isTrue(user != null, "用户不存在");
    String oldPassword = DigestUtils.md5DigestAsHex(
        StrUtil.bytes((request.getOldPassword() + SysUser.SALT), CharsetUtil.UTF_8));
    LuBootAssert.isTrue(oldPassword.equals(user.getPassword()), "老的密码输入有误");
    String newPassword = DigestUtils.md5DigestAsHex(
        StrUtil.bytes((request.getNewPassword() + SysUser.SALT), CharsetUtil.UTF_8));
    SysUser updateUser = new SysUser();
    updateUser.setId(user.getId());
    updateUser.setPassword(newPassword);
    updateUser.setUpdateUserId(loginUser.getId());
    updateUser.setUpdateTime(LocalDateTime.now());
    int res = this.baseMapper.updateById(updateUser);
    return res;
  }

  @Override
  public Integer updateStatus(Long id, Integer status) {
    LuBootAssert.isTrue(Arrays.stream(YesOrNoEnum.values()).anyMatch(o -> o.getValue() == status),
        "无效的状态参数");
    SysUser loginUser = (SysUser) SecurityContextHolder.getContext().getAuthentication()
        .getPrincipal();
    SysUser updateUser = new SysUser();
    updateUser.setId(id);
    updateUser.setStatus(status);
    updateUser.setUpdateUserId(loginUser.getId());
    updateUser.setUpdateTime(LocalDateTime.now());
    int res = this.baseMapper.updateById(updateUser);
    this.updateLoginUser(id);
    return res;
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public String deleteUser(Long id) {
    //删除用户
    int rs01 = this.baseMapper.deleteById(id);
    //删除用户的角色关系
    QueryWrapper<SysUserRole> userRoleQueryWrapper = new QueryWrapper<>();
    userRoleQueryWrapper.eq("user_id", id);
    int rs02 = userRoleMapper.delete(userRoleQueryWrapper);
    this.updateLoginUser(id);
    return rs01 + "-" + rs02;
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public String updateRoles(UserRolesUpdateRequest request) {
    SysUser loginUser = (SysUser) SecurityContextHolder.getContext().getAuthentication()
        .getPrincipal();
    QueryWrapper<SysUserRole> userRoleQueryWrapper = new QueryWrapper<>();
    userRoleQueryWrapper.eq("user_id", request.getUserId());
    int rs01 = userRoleMapper.delete(userRoleQueryWrapper);
    int rs02 = 0;
    for (Long roleId : request.getRoleIds()) {
      SysUserRole userRole = new SysUserRole();
      userRole.setUserId(request.getUserId());
      userRole.setRoleId(roleId);
      userRole.setUpdateUserId(loginUser.getId());
      userRole.setUpdateTime(LocalDateTime.now());
      int rs03 = userRoleMapper.insert(userRole);
      rs02 += rs03;
    }
    this.updateLoginUser(request.getUserId());
    return rs01 + "-" + rs02;
  }

  @Override
  public SysUser getByUserId(Long id) {
    SysUser user = this.baseMapper.selectById(id);
    LuBootAssert.isTrue(user != null, "不存在该用户");
    List<SysRole> roleList = roleMapper.getByUserId(id);
    user.setRoleList(roleList);
    return user;
  }

  @Override
  public List<UserRoleResourcesDTO.TreeNodes> getUserRoleMenuResources() {
    SysUser loginUser = (SysUser) SecurityContextHolder.getContext().getAuthentication()
        .getPrincipal();
    List<UserRoleResourcesDTO> resourcesDTOList = resourcesMapper
        .getUserRoleResources(loginUser.getId())
        .stream().filter(
            o -> ObjectUtil.isNotEmpty(o.getUrl()) && o.getType() == SysResources.Type.MENU
                .getType()).collect(Collectors.toList());
    return UserRoleResourcesDTO.treeNodes(resourcesDTOList.stream().collect(
        collectingAndThen(
            toCollection(() -> new TreeSet<>(Comparator.comparing(UserRoleResourcesDTO::getUrl))),
            ArrayList::new)), null);
  }

  @Override
  public List<UserRoleResourcesDTO> getUserRoleButtonResources() {
    SysUser loginUser = (SysUser) SecurityContextHolder.getContext().getAuthentication()
        .getPrincipal();
    List<UserRoleResourcesDTO> resourcesDTOList = resourcesMapper
        .getUserRoleResources(loginUser.getId())
        .stream().filter(
            o -> ObjectUtil.isNotEmpty(o.getUrl()) && o.getType() == SysResources.Type.BUTTON
                .getType()).collect(Collectors.toList());
    return resourcesDTOList;
  }

  @Override
  public LoginUser updateLoginUser(Long userId) {
    if (ObjectUtil.isEmpty(userId)) {
      return null;
    }
    if (redisTemplate.opsForValue().get(RedisKeyConstants.loginUser(userId)) == null) {
      return null;
    }
    //查询缓存的剩余过期时间
    Long expire = redisTemplate.opsForValue().getOperations()
        .getExpire(RedisKeyConstants.loginUser(userId));
    SysUser user = this.baseMapper.selectById(userId);
    if (user == null) {
      //删除缓存
      redisTemplate.delete(RedisKeyConstants.loginUser(userId));
      return null;
    }
    //查询用户角色资源信息
    List<UserRoleResourcesDTO> userRoleResources = resourcesMapper.getUserRoleResources(userId);
    if (ObjectUtil.isEmpty(userRoleResources)) {
      LoginUser loginUser = new LoginUser(user, true, true, true, true, null);
      redisTemplate.opsForValue()
          .set(RedisKeyConstants.loginUser(userId), JSON.toJSONString(loginUser), expire,
              TimeUnit.SECONDS);
      return null;
    }
    //登录用户 额外信息处理 商户、门店
    Integer userType = SysUserTypeEnum.SYS_USER.getType();
    SysMerchant sysMerchant = null;
    SysStore sysStore = null;
    QueryWrapper<SysStore> sysStoreQueryWrapper = new QueryWrapper<>();
    sysStoreQueryWrapper.select("id");
    sysStoreQueryWrapper.eq("user_id", SecurityUtil.getCurrentUser().getId());
    sysStore = sysStoreMapper.selectOne(sysStoreQueryWrapper);
    if (sysStore != null) {
      //登录账号若是门店
      userType = SysUserTypeEnum.SYS_STORE_USER.getType();
      ;
    } else {
      //登录账号若是商户
      QueryWrapper<SysMerchant> sysMerchantQueryWrapper = new QueryWrapper<>();
      sysMerchantQueryWrapper.eq("user_id", SecurityUtil.getCurrentUser().getId());
      sysMerchant = sysMerchantMapper.selectOne(sysMerchantQueryWrapper);
      if (sysMerchant != null) {
        userType = SysUserTypeEnum.SYS_MERCHANT_USER.getType();
        ;
      }
    }
    user.setUserType(userType);
    user.setMerchant(sysMerchant);
    user.setStore(sysStore);
    LoginUser customUserDetails = new LoginUser(user, true, true, true, true, userRoleResources);
    //更新缓存
    redisTemplate.opsForValue()
        .set(RedisKeyConstants.loginUser(userId), JSON.toJSONString(customUserDetails), expire,
            TimeUnit.SECONDS);
    return customUserDetails;
  }

  private void checkPhoneUnique(Long id, String phone) {
    if (ObjectUtil.isEmpty(phone)) {
      return;
    }
    QueryWrapper<SysUser> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.eq("phone", phone);
    if (ObjectUtil.isNotEmpty(id)) {
      userQueryWrapper.ne("id", id);
    }
    Integer count = this.baseMapper.selectCount(userQueryWrapper);
    LuBootAssert.isTrue(count == 0, "手机号不能重复");
  }

  private void checkUsernameUnique(Long id, String username) {
    if (ObjectUtil.isEmpty(username)) {
      return;
    }
    QueryWrapper<SysUser> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.eq("username", username);
    if (ObjectUtil.isNotEmpty(id)) {
      userQueryWrapper.ne("id", id);
    }
    Integer count = this.baseMapper.selectCount(userQueryWrapper);
    LuBootAssert.isTrue(count == 0, "账号不能重复");
  }

  @Override
  public Page<SysUser> pageList(UserQueryRequest request) {
    QueryWrapper<SysUser> userQueryWrapper = new QueryWrapper<>();
    if (ObjectUtil.isNotEmpty(request.getDeptId())) {
      if (request.getDeptId() != -1) {
        userQueryWrapper.eq("dept_id", request.getDeptId());
      } else {
        userQueryWrapper.isNull("dept_id");
        QueryWrapper<SysDept> deptQueryWrapper = new QueryWrapper<>();
        deptQueryWrapper.select("id");
        List<SysDept> depts = deptMapper.selectList(deptQueryWrapper);
        if (ObjectUtil.isNotEmpty(depts)) {
          userQueryWrapper.or().notIn("dept_id",
              depts.stream().map(SysDept::getId).collect(Collectors.toList()));
        }
      }
    }
    if (ObjectUtil.isNotEmpty(request.getGender())) {
      userQueryWrapper.eq("gender", request.getGender());
    }
    if (ObjectUtil.isNotEmpty(request.getLoginIp())) {
      userQueryWrapper.eq("login_ip", request.getLoginIp());
    }
    if (ObjectUtil.isNotEmpty(request.getUsername())) {
      userQueryWrapper.like("username", request.getUsername());
    }
    if (ObjectUtil.isNotEmpty(request.getRealName())) {
      userQueryWrapper.like("real_name", request.getRealName());
    }
    if (ObjectUtil.isNotEmpty(request.getPhone())) {
      userQueryWrapper.like("phone", request.getPhone());
    }
    if (ObjectUtil.isNotEmpty(request.getEmail())) {
      userQueryWrapper.like("email", request.getEmail());
    }
    if (ObjectUtil.isNotEmpty(request.getCompany())) {
      userQueryWrapper.like("company", request.getCompany());
    }
    if (ObjectUtil.isNotEmpty(request.getPosition())) {
      userQueryWrapper.like("position", request.getPosition());
    }
    if (ObjectUtil.isNotEmpty(request.getAddress())) {
      userQueryWrapper.like("address", request.getAddress());
    }
    if (ObjectUtil.isNotEmpty(request.getDescription())) {
      userQueryWrapper.like("description", request.getDescription());
    }
    userQueryWrapper.orderByDesc("create_time");
    Page<SysUser> userPage = this.baseMapper
        .selectPage(new Page<>(request.getPageIndex(), request.getPageSize()), userQueryWrapper);
    return userPage;
  }

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    //根据账号查询用户信息
    QueryWrapper<SysUser> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.eq("username", username);
    userQueryWrapper.eq("status", YesOrNoEnum.YES.getValue());
    SysUser user = this.baseMapper.selectOne(userQueryWrapper);
    LuBootAssert.isTrue(UsernameNotFoundException.class, user != null, "用户名密码不正确");
    //登录用户 额外信息处理 商户、门店
    Integer userType = SysUserTypeEnum.SYS_USER.getType();
    SysMerchant sysMerchant = null;
    SysStore sysStore = null;
    QueryWrapper<SysStore> sysStoreQueryWrapper = new QueryWrapper<>();
    sysStoreQueryWrapper.eq("user_id", user.getId());
    sysStore = sysStoreMapper.selectOne(sysStoreQueryWrapper);
    if (sysStore != null) {
      //登录账号若是门店
      userType = SysUserTypeEnum.SYS_STORE_USER.getType();
      ;
    } else {
      //登录账号若是商户
      QueryWrapper<SysMerchant> sysMerchantQueryWrapper = new QueryWrapper<>();
      sysMerchantQueryWrapper.eq("user_id", user.getId());
      sysMerchant = sysMerchantMapper.selectOne(sysMerchantQueryWrapper);
      if (sysMerchant != null) {
        userType = SysUserTypeEnum.SYS_MERCHANT_USER.getType();
        ;
      }
    }
    user.setUserType(userType);
    user.setMerchant(sysMerchant);
    user.setStore(sysStore);
    //查询用户角色资源信息
    List<UserRoleResourcesDTO> userRoleResources = resourcesMapper
        .getUserRoleResources(user.getId());
    LoginUser customUserDetails = new LoginUser(user, true, true, true, true, userRoleResources);
    return customUserDetails;
  }

}
